using SharpGraphT.Sharp.Sharp.Util;

namespace SharpGraphT.Graph;

[Serializable]
public class GraphDelegator<TV, TE> : AbstractGraph<TV, TE>, IGraph<TV, TE>
    where TE : class, new() {
    private static readonly long SerialVersionUid = -215068279981825448L;

    private readonly IGraph<TV, TE> _delegate;
    private readonly Func<TV> _vertexSupplier;
    private readonly Func<TE> _edgeSupplier;

    public GraphDelegator(IGraph<TV, TE> graph) : this(graph, null, null) { }

    public GraphDelegator(IGraph<TV, TE> graph, Func<TV> vertexSupplier,
        Func<TE> edgeSupplier) {
        _delegate = Objects.RequireNonNull(graph, "graph must not be null");
        _vertexSupplier = vertexSupplier;
        _edgeSupplier = edgeSupplier;
    }

    public override Func<TV> VertexSupplier =>
        _vertexSupplier == null ? _delegate.VertexSupplier : _vertexSupplier;

    public override Func<TE> EdgeSupplier =>
        _edgeSupplier == null ? _delegate.EdgeSupplier : _edgeSupplier;

    public override IReadOnlySet<TE> GetAllEdges(TV sourceVertex,
        TV targetVertex) =>
        _delegate.GetAllEdges(sourceVertex, targetVertex);

    public override TE GetEdge(TV sourceVertex, TV targetVertex) =>
        _delegate.GetEdge(sourceVertex, targetVertex);

    public override TE AddEdge(TV sourceVertex, TV targetVertex) {
        if (_edgeSupplier == null) {
            return _delegate.AddEdge(sourceVertex, targetVertex);
        }

        var e = _edgeSupplier();
        return AddEdge(sourceVertex, targetVertex, e) ? e : null;
    }

    public override bool AddEdge(TV sourceVertex, TV targetVertex, TE e) =>
        _delegate.AddEdge(sourceVertex, targetVertex, e);

    public override TV AddVertex() {
        if (_vertexSupplier == null) {
            return _delegate.AddVertex();
        }

        var v = _vertexSupplier();
        Objects.RequireNonDefault(v,
            "Vertex of a default value should not be added");

        return AddVertex(v) ? v : default;
    }

    public override bool AddVertex(TV v) {
        Objects.RequireNonDefault(v,
            "Vertex of a default value should not be added");

        return _delegate.AddVertex(v);
    }

    public override bool ContainsEdge(TE e) => _delegate.ContainsEdge(e);

    public override bool ContainsVertex(TV v) => _delegate.ContainsVertex(v);

    public override int DegreeOf(TV vertex) => _delegate.DegreeOf(vertex);

    public override IReadOnlySet<TE> EdgeSet => _delegate.EdgeSet;

    public override IReadOnlySet<TE> EdgesOf(TV vertex) =>
        _delegate.EdgesOf(vertex);

    public override int InDegreeOf(TV vertex) => _delegate.InDegreeOf(vertex);

    public override IReadOnlySet<TE> IncomingEdgesOf(TV vertex) =>
        _delegate.IncomingEdgesOf(vertex);

    public override int OutDegreeOf(TV vertex) => _delegate.OutDegreeOf(vertex);

    public override IReadOnlySet<TE> OutgoingEdgesOf(TV vertex) =>
        _delegate.OutgoingEdgesOf(vertex);

    public override bool RemoveEdge(TE e) => _delegate.RemoveEdge(e);

    public override TE RemoveEdge(TV sourceVertex, TV targetVertex) =>
        _delegate.RemoveEdge(sourceVertex, targetVertex);

    public override bool RemoveVertex(TV v) => _delegate.RemoveVertex(v);

    public override string ToString() => _delegate.ToString();

    public override IReadOnlySet<TV> VertexSet => _delegate.VertexSet;

    public override TV GetEdgeSource(TE e) => _delegate.GetEdgeSource(e);

    public override TV GetEdgeTarget(TE e) => _delegate.GetEdgeTarget(e);

    public override double GetEdgeWeight(TE e) => _delegate.GetEdgeWeight(e);

    public override void SetEdgeWeight(TE e, double weight) =>
        _delegate.SetEdgeWeight(e, weight);

    public override IGraphType Type => _delegate.Type;

    protected IGraph<TV, TE> Delegate => _delegate;
}